package kandl.automation.steprunner.selenium;

import com.google.common.base.Function;

import org.apache.log4j.Logger;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

import kandl.automation.steprunner.StepRunner;
import kandl.automation.testsuite.TestProfile;
import kandl.automation.testsuite.TestStep;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Maps.newHashMap;

public class SeleniumStepRunner implements StepRunner {

    private static final Logger logger = Logger.getLogger(SeleniumStepRunner.class);

    private static final String URL_SUBSTITUTION = "vURL";

    private final Map<TestProfile, WebDriver> drivers;

    private final Map<String, Command> commandMapper = newHashMap();

    private final WebDriverFactory driverFactory;

    private final SimpleDateFormat simpleDateFormat;

    private final Date date;

    public SeleniumStepRunner(WebDriverFactory driverFactory, List<Command> commands) {
        for(Command command: commands) {
            commandMapper.put(command.getName(), command);
        }
        this.driverFactory = driverFactory;
        this.drivers = new HashMap<TestProfile, WebDriver>();
        simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy-kk:mm:ss", Locale.ENGLISH);
        date = new Date();
    }

    @Override
    public void execute(TestStep step, TestProfile profile) throws Exception {

        String keyword = step.getKeyword();

        if(!commandMapper.containsKey(keyword)) {
            throw new IllegalArgumentException("Keyword " + keyword + " is not supported.");
        }

        String[] params = processParams(step.getParameters(), profile);

        WebDriver driver = getWebDriver(profile);

        commandMapper.get(keyword).runCommand(driver, params);
    }

    private String[] processParams(String[] parameters, final TestProfile profile) {
        return transform(Arrays.asList(parameters), new Function<String, String>(){
            @Override
            public String apply(String param) {
                return param.equals(URL_SUBSTITUTION) ? profile.getUrl() : param;
            }
        }).toArray(parameters);
    }
    
    @Override
    public void finish(TestProfile profile) {
        synchronized(drivers) {
            WebDriver driver = drivers.get(profile);
            if (driver != null) {
                driver.quit();
                drivers.remove(profile);
            }
        }
    }

    private WebDriver getWebDriver(TestProfile profile) {
        synchronized(drivers) {
            WebDriver driver = drivers.get(profile);
            if (driver == null) {
                driver = driverFactory.getWebDriver(profile);
                drivers.put(profile, driver);
            }
            return driver;
        }
    }

    public String screenshot(TestStep step, TestProfile profile) {
        try {
            WebDriver driver = getWebDriver(profile);
            if (!TakesScreenshot.class.isAssignableFrom(driver.getClass())) {
                logger.error("WebDriver does not support taking screenshots.");
            }
            else {
                String path = "target/screenshots/" + simpleDateFormat.format(date) + "/" + profile.getId() + "_" + step.getId() + ".png";
                byte[] screenshot = ((TakesScreenshot)getWebDriver(profile)).getScreenshotAs(OutputType.BYTES);
                File f = new File(path);
                if (f.getParentFile().mkdirs()) {
                    OutputStream out = new FileOutputStream(f);
                    out.write(screenshot);
                    out.flush();
                    out.close();
                    return path;
                }
            }
        } catch (IOException e) {
            logger.error("Cannot write image for step " + step.getId(), e);
        }
        return null;
    }
}
